package cn.jiuri.jchat.api;


import cn.hutool.core.lang.Singleton;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.*;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import cn.jiuri.jchat.common.CodeEnum;
import cn.jiuri.jchat.common.Constant;
import cn.jiuri.jchat.common.Core;
import cn.jiuri.jchat.model.Msg;
import cn.jiuri.jchat.model.User;
import cn.jiuri.jchat.msg.HandleMsgInterface;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.io.*;
import java.net.HttpCookie;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 错误返回值分析：
 * {"Ret": 0,"ErrMsg": ""} 成功
 * {"Ret": -14,"ErrMsg": ""} ticket 错误
 * {"Ret": 1,"ErrMsg": ""} 传入参数 错误
 * {"Ret": 1100"ErrMsg": ""}未登录提示
 * {"Ret": 1101,"ErrMsg": ""}（可能：1未检测到登陆？）
 * {"Ret": 1102,"ErrMsg": ""}（可能：cookie值无效？）
 */
public class API {
    static Log log = LogFactory.get();
    private static Core core = Singleton.get(Core.class);
    //login
    public static void run() {
        //获取UUID
        String uuid = getUuid();
        //登陆二维码下载到本地
        showQr(uuid, core.getQrCodePath());
        //打开登陆二维码
        openImg(core.getQrCodePath());
        //检测登陆
        String result = waitLogin(uuid);
        //删除登陆二维码
        delQrCode(core.getQrCodePath());
        //获取cookie
        cookie(result);
        //初始化微信
        weChatInit();
        //获取通讯录列表
        contact();
        //检测并接收消息
        checkAndGetMsg();
    }


    /**
     * 1.获取UUID
     *
     * @return
     */
    public static String getUuid() {
        String body = "";
        try {
            body = HttpUtil.createGet(Constant.GET_UUID_API)
                    .keepAlive(true)
                    .form("appid", Constant.APP_ID)
                    .form("fun", "new")
                    .form("lang", "zh_CN")
                    .form("_", System.currentTimeMillis())
                    .setSSLSocketFactory(core.getSsf())
                    .execute()
                    .body();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!body.contains("200") || !body.contains("window.QRLogin.uuid")) {
            throw new RuntimeException();
        }
        String[] split = body.split("\"");
        return split[1];
    }

    /**
     * 2.获取web登陆二维码
     *
     * @param uuid
     * @param path
     */
    public static void showQr(String uuid, String path) {
        try {
            File file = createFile(path);
            HttpUtil.createGet(String.format(Constant.SHOW_QR_CODE_API, uuid))
                    .form("t", "webwx")
                    .keepAlive(true)
                    .setSSLSocketFactory(core.getSsf())
                    .execute()
                    .writeBody(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 3.检测登陆
     *
     * @param uuid
     * @return
     */
    public static String waitLogin(String uuid) {
        int i = 10;
        while (true) {
            log.info("登陆检测倒计时.....:{}", i);
            if (i <= 0) {
                throw new RuntimeException();
            }
            i--;
            String body = "";
            try {
                body = HttpUtil.createGet(Constant.WAIT_LOGIN_API)
                        .form("uuid", uuid)
                        .keepAlive(true)
                        .setSSLSocketFactory(core.getSsf())
                        .execute()
                        .body();
            } catch (Exception e) {
                return waitLogin(uuid);
            }
            if (body.contains("200")) {
                String result = body.split("redirect_uri=")[1];
                String url = result.replaceAll("\"", "").replaceAll(";", "");
                log.info("登陆成功URL:{}", url);
                return url;
            }
            log.info("用户已扫描");
        }
    }

    /**
     * 4.登陆成功获取Cookie
     *
     * @param url
     * @return
     */
    public static boolean cookie(String url) {
        HttpResponse response = null;
        try {
            response = HttpUtil.createGet(url)
                    .keepAlive(true)
                    .setSSLSocketFactory(core.getSsf())
                    .execute();
        } catch (Exception e) {
            e.printStackTrace();
        }
        String result = response.body();
        Document document = XmlUtil.readXML(result);
        Element rootElement = XmlUtil.getRootElement(document);
        String skey = XmlUtil.getElement(rootElement, "skey").getTextContent();
        String wxsid = XmlUtil.getElement(rootElement, "wxsid").getTextContent();
        String wxuin = XmlUtil.getElement(rootElement, "wxuin").getTextContent();
        String passTicket = XmlUtil.getElement(rootElement, "pass_ticket").getTextContent();
        JSONObject json = new JSONObject();
        json.put("Uin", wxuin);
        json.put("Sid", wxsid);
        json.put("Skey", skey);
        json.put("pass_ticket", passTicket);
        JSONObject json1 = new JSONObject();
        json1.put("BaseRequest", json);
        core.setParamMap(json1);
        List<HttpCookie> cookies = response.getCookies();
        StringBuffer sb = new StringBuffer();
        for (HttpCookie cookie : cookies) {
            sb.append(cookie.getName());
            sb.append("=");
            sb.append(cookie.getValue());
            sb.append(";");
        }
        core.setLogin(true);
        core.setCookie(sb.toString());
        return true;
    }


    /**
     * 5.微信初始化
     *
     * @return
     */
    public static void weChatInit() {
        JSONObject paramMap = core.getParamMap();
        JSONObject baseRequest = new JSONObject(paramMap.get("BaseRequest"));
        String body = "";
        try {
            body = HttpUtil.createPost(String.format(Constant.WX_INIT_API, baseRequest.get("pass_ticket")))
                    .header("Content-Type", "application/json; charset=UTF-8")
                    .body(JSONUtil.toJsonStr(paramMap))
                    .keepAlive(true)
                    .setSSLSocketFactory(core.getSsf())
                    .timeout(5000)
                    .execute()
                    .body();
        } catch (Exception e) {
            e.printStackTrace();
        }
        JSONObject jsonObject = new JSONObject(body);
        JSONObject user = new JSONObject(jsonObject.get("User"));
        core.setUserName(user.getStr("UserName"));
        core.setNickName(user.getStr("NickName"));
        paramMap.put("SyncKey", jsonObject.get("SyncKey"));
    }

    /**
     * 6.开启微信通知
     *
     * @return
     */
    public static JSONObject wxStatusNotify() {
        JSONObject paramMap = core.getParamMap();
        paramMap.put("Code", 3);
        paramMap.put("FromUserName", core.getUserName());
        paramMap.put("ToUserName", core.getUserName());
        paramMap.put("ClientMsgId", System.currentTimeMillis());
        String body = "";
        try {
            body = HttpUtil.createPost(String.format(Constant.WX_STATUS_NOTIFY, "zh_CN", paramMap.get("pass_ticket")))
                    .header("ContentType", "application/json; charset=UTF-8")
                    .setSSLSocketFactory(core.getSsf())
                    .body(JSONUtil.toJsonStr(paramMap))
                    .header("Connection", "keep-alive")
                    .execute()
                    .body();
        } catch (Exception e) {
            e.printStackTrace();
        }


        JSONObject jsonObject = new JSONObject(body);
        return jsonObject;
    }

    /**
     * 7.获取微信好友列表
     *
     * @return
     */
    public static Map<String, User> contact() {
        JSONObject paramMap = core.getParamMap();
        JSONObject baseRequest = new JSONObject(paramMap.get("BaseRequest"));
        String body = "";
        try {
            body = HttpUtil.createPost(String.format(Constant.WX_CONTACT, baseRequest.get("pass_ticket"), baseRequest.get("Skey")))
                    .header("ContentType", "application/json; charset=UTF-8")
                    .keepAlive(true)
                    .setSSLSocketFactory(core.getSsf())
                    .body(JSONUtil.toJsonStr(paramMap))
                    .timeout(5000)
                    .execute()
                    .body();
        } catch (Exception e) {
            e.printStackTrace();
        }

        JSONObject jsonObject = new JSONObject(body);
        JSONArray jsonArray = new JSONArray(jsonObject.get("MemberList"));
        ConcurrentHashMap<String, User> map = new ConcurrentHashMap();
        for (Object o : jsonArray) {
            JSONObject userJSON = new JSONObject(o);
            User user = new User();
            user.setUserName(userJSON.getStr("UserName"));
            user.setNickName(userJSON.getStr("NickName"));
            user.setSex(userJSON.getInt("Sex"));
            user.setCity(userJSON.getStr("City"));
            user.setHeadImgUrl(userJSON.getStr("HeadImgUrl"));
            user.setProvince(userJSON.getStr("Province"));
            user.setRemarkName(userJSON.getStr("RemarkName"));
            user.setSignature(userJSON.getStr("Signature"));
            map.put(user.getUserName(), user);
        }
        core.setUserMap(map);
        return map;
    }

    /**
     * 8.检测新消息并获取
     */
    public static void checkAndGetMsg() {
        new Thread(() -> {
            while (core.isLogin()) {
                JSONObject jsonObject = checkMsg();
                if (jsonObject.getStr("retcode").equals("0")
                        && jsonObject.getStr("selector").equals("2")
                        || jsonObject.getStr("selector").equals("6")) {
                    JSONObject msgJSON = getMsg();
                    JSONArray addMsgList = new JSONArray(msgJSON.getStr("AddMsgList"));
                    Queue<Msg> msgQueue = core.getMsgQueue();
                    for (Object o : addMsgList) {
                        JSONObject jsonObject1 = new JSONObject(o);
                        String content = jsonObject1.getStr("Content");
                        String fromUserName = jsonObject1.getStr("FromUserName");
                        String toUserName = jsonObject1.getStr("ToUserName");
                        if (StrUtil.isNotBlank(content)
                                //不是自己
                                && !fromUserName.equals(core.getUserName())) {
                            Msg msg = new Msg();
                            msg.setContent(content);
                            msg.setFromUserName(fromUserName);
                            msg.setToUserName(toUserName);
                            msgQueue.add(msg);
                        }
                    }
                }
                if (jsonObject.getStr("retcode").equals("1101")) {
                    log.info("账号已在别处登陆,程序退出...");
                    System.exit(0);
                }
            }
        }).start();
    }

    /**
     * 检测是否有新消息
     *
     * @return
     */
    public static JSONObject checkMsg() {
        JSONObject paramMap = core.getParamMap();
        JSONObject baseRequest = new JSONObject(paramMap.get("BaseRequest"));
        JSONObject syncKey = new JSONObject(paramMap.get("SyncKey"));
        JSONArray jsonArray = new JSONArray(syncKey.get("List"));
        String syncKeyStr = getSyncKey(jsonArray);
        String url = String.format(Constant.WX_CHECK_MSG, System.currentTimeMillis(), baseRequest.get("Skey"), baseRequest.get("Sid"), baseRequest.get("Uin"), getDeviceID(), syncKeyStr, System.currentTimeMillis());
        String body = HttpUtil.createGet(url)
                .header("Cookie", core.getCookie())
                .setSSLSocketFactory(core.getSsf())
                .execute()
                .body();
        String[] split = body.split("\"");
        String retCode = split[1];
        String selector = split[3];
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("retcode", retCode);
        jsonObject.put("selector", selector);
        return jsonObject;
    }

    /**
     * 获取新消息
     *
     * @return
     */
    public static JSONObject getMsg() {
        JSONObject paramMap = core.getParamMap();
        JSONObject baseRequest = new JSONObject(paramMap.get("BaseRequest"));
        paramMap.put("rr", System.currentTimeMillis() / 1000);
        String body = HttpUtil.createPost(String.format(Constant.WX_GET_MSG, baseRequest.get("Sid"), baseRequest.get("Skey"), baseRequest.get("pass_ticket")))
                .header("ContentType", "application/json; charset=UTF-8")
                .header("Cookie", core.getCookie())
                .setSSLSocketFactory(core.getSsf())
                .body(paramMap.toJSONString(0))
                .execute()
                .body();
        JSONObject msg = new JSONObject(body);
        paramMap.put("SyncKey", msg.get("SyncCheckKey"));
        return msg;
    }

    /**
     * 消息处理
     *
     * @param handleMsg
     */
    public static void handleMsg(HandleMsgInterface handleMsg) {
        new Thread(() -> {
            while (true) {
                Queue<Msg> msgQueue = core.getMsgQueue();
                if (msgQueue != null && msgQueue.size() > 0) {
                    Msg msg = msgQueue.poll();
                    if (msg != null) {
                        handleMsg.handleMsg(msg);
                    }
                }
            }
        }).start();
    }

    /**
     * 获取群成员
     */
    public static ArrayList<User> getGroupInfo(String username) {
        //参数1
        JSONArray jsonArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("UserName", username);
        jsonObject.put("EncryChatRoomId", "");
        jsonArray.add(0, jsonObject);
        //参数2
        JSONObject paramMap = core.getParamMap();
        JSONObject baseRequest = new JSONObject(paramMap.get("BaseRequest"));
        //参数总
        JSONObject jsonObject1 = new JSONObject();
        jsonObject1.put("List", jsonArray);
        jsonObject1.put("Count", 1);
        jsonObject1.put("BaseRequest", baseRequest);
        String body = "";
        try {
            body = HttpUtil.createPost(String.format(Constant.WX_GROUP_INFO, baseRequest.get("pass_ticket")))
                    .header("ContentType", "application/json; charset=UTF-8")
                    .keepAlive(true)
                    .setSSLSocketFactory(core.getSsf())
                    .body(JSONUtil.toJsonStr(jsonObject1))
                    .timeout(5000)
                    .execute()
                    .body();
        } catch (Exception e) {
            e.printStackTrace();
        }
        ArrayList<User> list = new ArrayList<>();
        JSONObject jsonObject2 = JSONUtil.parseObj(body);
        if (jsonObject2.getJSONObject("BaseResponse").getInt("Ret").equals(0)) {
            JSONArray contactList = jsonObject2.getJSONArray("ContactList");
            JSONObject jsonObject3 = new JSONObject(contactList.get(0));
            JSONArray memberList = jsonObject3.getJSONArray("MemberList");
            for (Object o : memberList) {
                User user = JSONUtil.toBean(o.toString(), User.class);
                user.setHeadImgUrl(String.format(Constant.WX_HEAD_IMG, user.getUserName(), baseRequest.get("Skey"), jsonObject3.getStr("EncryChatRoomId")));
                list.add(user);
            }
            return list;
        }
        return null;
    }

    /**
     * 9.发送消息(暂时仅支持文本格式消息)
     *
     * @param content  消息内容
     * @param userName 接受人name
     * @param type     消息类型 1:文本格式
     * @return
     */
    public static boolean sendMsg(String content, String userName, Integer type) {
        JSONObject paramMap = core.getParamMap();
        JSONObject baseRequest = new JSONObject(paramMap.get("BaseRequest"));
        Msg msg = new Msg();
        msg.setContent(content);
        msg.setFromUserName(core.getUserName());
        msg.setToUserName(userName);
        msg.setLocalID(System.currentTimeMillis());
        if (type.equals(CodeEnum.TEXT.getCode())) {
            msg.setType(CodeEnum.TEXT.getCode());
        }
        paramMap.put("Msg", msg);
        try {
            HttpUtil.createPost(String.format(Constant.WX_SEND_MSG, baseRequest.get("pass_ticket")))
                    .header("ContentType", "application/json; charset=UTF-8")
                    .header("Cookie", core.getCookie())
                    .setSSLSocketFactory(core.getSsf())
                    .body(paramMap.toJSONString(0))
                    .keepAlive(true)
                    .execute()
                    .body();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
    //打开二维码图片
    public static void openImg(String path) {
        Runtime runtime = Runtime.getRuntime();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(runtime.exec("open " + path).getInputStream()));
            String line = null;
            StringBuffer b = new StringBuffer();
            while ((line = br.readLine()) != null) {
                b.append(line + "\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static File createFile(String path) throws IOException {
        File file = new File(path);
        file.createNewFile();
        return file;
    }

    private static void delQrCode(String path) {
        File file = new File(path);
        file.delete();
    }

    public static String getSyncKey(JSONArray jsonArray) {
        Object[] objects = jsonArray.toArray();
        StringBuilder sb = new StringBuilder();
        for (Object object : objects) {
            JSONObject jsonObject = new JSONObject(object);
            String val = jsonObject.getStr("Val");
            String key = jsonObject.getStr("Key");
            sb.append(key).append("_").append(val).append("|");
        }
        return sb.substring(0, sb.toString().length() - 1);
    }

    public static String getDeviceID() {
        return "e" + String.valueOf(new Random().nextLong()).substring(1, 16);
    }
}
